package main

import(
	"fmt"
	"os"
	"os/exec"
	"io"
	"github.com/spf13/pflag"
)

func main() {
	var start int
	var end int
	var line int
	var framefeed bool
	var destination string
	var mywriter io.Writer = os.Stdout
	var pr *io.PipeReader
	var pw *io.PipeWriter

	pflag.IntVarP(&start, "s", "s", -1, "start page")
	pflag.IntVarP(&end, "e", "e", -1, "end page")
	pflag.IntVarP(&line, "l", "l", 72, "lines per page")
	pflag.BoolVarP(&framefeed, "f", "f", false, "is separated by FF")
	pflag.StringVarP(&destination, "d", "d", "", "the -d flag of lp")
	pflag.Parse()

	if start == -1 || end == -1 {
		fmt.Fprintln(os.Stderr, "-s and -e must appear")
		os.Exit(1)
	}
	if start <= 0 || end <= 0 {
		fmt.Fprintln(os.Stderr, "start page and end page must be positive")
		os.Exit(1)
	}
	if start > end {
		fmt.Fprintln(os.Stderr, "start page cannot exceed end page")
		os.Exit(1)
	}

	if line <= 0 {
		fmt.Fprintln(os.Stderr, "line must be positive")
		os.Exit(1)
	}

	args := pflag.Args()
	numArgs := len(args)
	if numArgs > 1 {
		fmt.Fprintln(os.Stderr, "at most 1 argument can be given")
		os.Exit(1)
	}
	if numArgs == 1 {
		f, e := os.Open(args[0])
		if e != nil {
			fmt.Fprintf(os.Stderr, "cannot open file %s\n", args[0])
			os.Exit(1)
		}
		os.Stdin = f
	}

	if destination != "" {
		pr,pw = io.Pipe()
		mywriter = pw

		go func() {
			out, e := os.OpenFile(destination, os.O_CREATE | os.O_TRUNC | os.O_WRONLY, 0755)
			if e != nil {
				fmt.Fprintf(os.Stderr, "cannot open file %s\n", destination)
				os.Exit(1)
			}
			cmd := exec.Command("cat")
			cmd.Stdout = out
			cmd.Stdin = pr
			cmd.Run()
		}()
	}

	buffer_read := make([]byte, 4096)
	buffer_write := []byte{}

	var bytes_read int
	var e error
	current_page := 1
	current_line := 0
	for true {
		bytes_read, e = os.Stdin.Read(buffer_read)
		if e != nil && e != io.EOF {
			fmt.Fprintln(os.Stderr, "error while reading")
			fmt.Fprintln(os.Stderr, e)
			os.Exit(1)
		}
		if bytes_read == 0 {
			break
		}

		for i := 0; i < bytes_read; i++ {
			ch := buffer_read[i]
			old_page := current_page
			if framefeed {
				if buffer_read[i] == '\f' {
					current_page++
					ch = '\n'
				}
			} else {
				if buffer_read[i] == '\n' {
					current_line++
					if current_line == line {
						current_line = 0
						current_page++
					}
				}
			}
			if old_page >= start && old_page <= end {
				buffer_write = append(buffer_write, ch)
				if len(buffer_write) >= 4096 {
					mywriter.Write(buffer_write)
					buffer_write = []byte{}
				}
			}
		}
	}
	if len(buffer_write) > 0 {
		mywriter.Write(buffer_write)
	}
	os.Stdin.Close()
	os.Stdout.Close()
	if pw != nil {
		pw.Close()
	}
}
